﻿using NUnit.Framework;
using Sasa.QualityTools.CodeAnalysis.SearchEngine.Processing;
using Sasa.QualityTools.CodeAnalysis.SearchEngine.Processing.Tokens;
using Sasa.QualityTools.CodeAnalysis.SearchEngine.Tests.Unit.Helpers;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Sasa.QualityTools.CodeAnalysis.SearchEngine.Tests.Unit.Processing
{
    [TestFixture]
    public class PreprocessorTest
    {
        private Preprocessor testee;


        [SetUp]
        public void SetUp()
        {
            this.testee = new Preprocessor();
        }

        [Test]
        public void TestComplementParenthesesWithNull()
        {
            CollectionAssert.IsEmpty(CallComplementParentheses(null));
        }

        [Test]
        public void TestComplementParenthesesWithEmpty()
        {
            CollectionAssert.IsEmpty(CallComplementParentheses(new List<IToken>()));
        }

        [Test]
        public void TestComplementParenthesesWithTokensDoNotHavePar()
        {
            var tokens = Tokens().Word("word");
            var expected = Tokens().Word("word");
            CollectionAssert.AreEqual(expected, CallComplementParentheses(tokens));
        }

        [Test]
        public void TestComplementParenthesesWithTokensHaveConjugatedPar()
        {
            var tokens = Tokens().LPar().Word("word").RPar();
            var expected = Tokens().LPar().Word("word").RPar();
            CollectionAssert.AreEqual(expected, CallComplementParentheses(tokens));
        }

        [Test]
        public void TestComplementParenthesesWithTokensHaveUnconjugatedPar()
        {
            var tokens = Tokens().RPar().Word("word").LPar();
            var expected = Tokens().LPar().RPar().Word("word").LPar().RPar();
            CollectionAssert.AreEqual(expected, CallComplementParentheses(tokens));
        }

        [Test]
        public void TestComplementParenthesesWithTokensLackRPar()
        {
            var tokens = Tokens().LPar().Word("word");
            var expected = Tokens().LPar().Word("word").RPar();
            CollectionAssert.AreEqual(expected, CallComplementParentheses(tokens));
        }

        [Test]
        public void TestComplementParenthesesWithTokensLackMultiRPar()
        {
            var tokens = Tokens().LPar().Word("word1").LPar().Word("word2");
            var expected = Tokens().LPar().Word("word1").LPar().Word("word2").RPar().RPar();
            CollectionAssert.AreEqual(expected, CallComplementParentheses(tokens));
        }

        [Test]
        public void TestComplementParenthesesWithTokensLackLPar()
        {
            var tokens = Tokens().Word("word").RPar();
            var expected = Tokens().LPar().Word("word").RPar();
            CollectionAssert.AreEqual(expected, CallComplementParentheses(tokens));
        }

        [Test]
        public void TestComplementParenthesesWithTokensLackMultiLPar()
        {
            var tokens = Tokens().Word("word1").RPar().Word("word2").RPar();
            var expected = Tokens().LPar().LPar().Word("word1").RPar().Word("word2").RPar();
            CollectionAssert.AreEqual(expected, CallComplementParentheses(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithWordAndWord()
        {
            var tokens = Tokens().Word("word1").Word("word2");
            var expected = Tokens().Word("word1").And("AND").Word("word2");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithWordAndAND()
        {
            var tokens = Tokens().Word("word1").And("AND");
            var expected = Tokens().Word("word1").And("AND").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithWordAndOR()
        {
            var tokens = Tokens().Word("word1").Or("OR");
            var expected = Tokens().Word("word1").Or("OR").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithWordAndLPar()
        {
            var tokens = Tokens().Word("word1").LPar();
            var expected = Tokens().Word("word1").And("AND").LPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithWordAndRPar()
        {
            var tokens = Tokens().Word("word1").RPar();
            var expected = Tokens().Word("word1").RPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithANDAndWord()
        {
            var tokens = Tokens().And("AND").Word("word1");
            var expected = Tokens().Word("").And("AND").Word("word1");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithANDAndAND()
        {
            var tokens = Tokens().And("AND").And("AND");
            var expected = Tokens().Word("").And("AND").Word("").And("AND").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithANDAndOR()
        {
            var tokens = Tokens().And("AND").Or("OR");
            var expected = Tokens().Word("").And("AND").Word("").Or("OR").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithANDAndLPar()
        {
            var tokens = Tokens().And("AND").LPar();
            var expected = Tokens().Word("").And("AND").LPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithANDAndRPar()
        {
            var tokens = Tokens().And("AND").RPar();
            var expected = Tokens().Word("").And("AND").Word("").RPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithORAndWord()
        {
            var tokens = Tokens().Or("OR").Word("word1");
            var expected = Tokens().Word("").Or("OR").Word("word1");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithORAndAND()
        {
            var tokens = Tokens().Or("OR").And("AND");
            var expected = Tokens().Word("").Or("OR").Word("").And("AND").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithORAndOR()
        {
            var tokens = Tokens().Or("OR").Or("OR");
            var expected = Tokens().Word("").Or("OR").Word("").Or("OR").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithORAndLPar()
        {
            var tokens = Tokens().Or("OR").LPar();
            var expected = Tokens().Word("").Or("OR").LPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithORAndRPar()
        {
            var tokens = Tokens().Or("OR").RPar();
            var expected = Tokens().Word("").Or("OR").Word("").RPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithLParAndWord()
        {
            var tokens = Tokens().LPar().Word("word1");
            var expected = Tokens().LPar().Word("word1");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithLParAndAND()
        {
            var tokens = Tokens().LPar().And("AND");
            var expected = Tokens().LPar().Word("").And("AND").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithLParAndOR()
        {
            var tokens = Tokens().LPar().Or("OR");
            var expected = Tokens().LPar().Word("").Or("OR").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithLParAndLPar()
        {
            var tokens = Tokens().LPar().LPar();
            var expected = Tokens().LPar().LPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithLParAndRPar()
        {
            var tokens = Tokens().LPar().RPar();
            var expected = Tokens().LPar().Word("").RPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithRParAndWord()
        {
            var tokens = Tokens().RPar().Word("word1");
            var expected = Tokens().RPar().And("AND").Word("word1");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithRParAndAND()
        {
            var tokens = Tokens().RPar().And("AND");
            var expected = Tokens().RPar().And("AND").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithRParAndOR()
        {
            var tokens = Tokens().RPar().Or("OR");
            var expected = Tokens().RPar().Or("OR").Word("");
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithRParAndLPar()
        {
            var tokens = Tokens().RPar().LPar();
            var expected = Tokens().RPar().And("AND").LPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        [Test]
        public void TestComplementAbbrevedTokensWithRParAndRPar()
        {
            var tokens = Tokens().RPar().RPar();
            var expected = Tokens().RPar().RPar();
            CollectionAssert.AreEqual(expected, CallComplementAbbrevedTokens(tokens));
        }

        private IEnumerable<IToken> CallComplementParentheses(IEnumerable<IToken> tokens)
        {
            MethodInfo method = this.testee.GetType().GetMethod("ComplementParentheses", BindingFlags.Instance | BindingFlags.NonPublic);
            return (IEnumerable<IToken>)method.Invoke(this.testee, new object[] { tokens });
        }

        private IEnumerable<IToken> CallComplementAbbrevedTokens(IEnumerable<IToken> tokens)
        {
            MethodInfo method = this.testee.GetType().GetMethod("ComplementAbbrevedTokens", BindingFlags.Instance | BindingFlags.NonPublic);
            return (IEnumerable<IToken>)method.Invoke(this.testee, new object[] { tokens });
        }

        private IEnumerable<IToken> Tokens()
        {
            return new List<IToken>();
        }
    }
}
